package com.isight.security;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.isight.dao.AuthorityDao;
import com.isight.dao.UserDao;
import com.isight.domain.Authority;
import com.isight.domain.User;

@Service("jpaUserDetailsService")
public class UserDetailsServiceJpaImpl implements UserDetailsService {
    
    public static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceJpaImpl.class);
    
    public static final String FIND_USERS_BY_USERNAME_JQUERY = 
        " select u from User u " +
        " where u.username = :name ";
    public static final String FIND_AUTHORITIES_BY_USERNAME_JQUERY = 
        " select a from Authority " + 
        " where u.username = :name ";
    
    private UserDao userDao;
    private AuthorityDao authorityDao;
    
    @Autowired
    @Qualifier("userDao")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    @Autowired
    @Qualifier("authorityDao")
    public void setAuthorityDao(AuthorityDao authorityDao) {
        this.authorityDao = authorityDao;
    }
    
    private String authoritiesByUsernameJQuery;
    private String groupAuthoritiesByUsernameJQuery;
    private String usersByUsernameJQuery;
    private String rolePrefix = "";
    private boolean usernameBasedPrimaryKey = true;
    private boolean enableAuthorities = true;
    private boolean enableGroups;
    
    public UserDetailsServiceJpaImpl() {
        usersByUsernameJQuery = FIND_USERS_BY_USERNAME_JQUERY;
        authoritiesByUsernameJQuery = FIND_AUTHORITIES_BY_USERNAME_JQUERY;
        groupAuthoritiesByUsernameJQuery = "";
    }
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        List<UserDetails> userDetailsList = loadUsersByUsername(username);
        
        if (userDetailsList.size() == 0) {
            logger.debug("Query returned no results for user '" + username + "'");
            throw new UsernameNotFoundException("Username " + username + " not found");
        }
        
        UserDetails account = userDetailsList.get(0);
        
        Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();
        
        if (enableAuthorities) {
            dbAuthsSet.addAll(loadUserAuthorities(account.getUsername()));
        }
        
        if (enableGroups) {
            dbAuthsSet.addAll(loadGroupAuthorities(account.getUsername()));
        }
        
        List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);
        
        addCustomAuthorities(account.getUsername(), dbAuths);
        
        if (dbAuths.size() == 0) {
            logger.debug("user '" + username + "' has no authorities and will be treated as 'not found'");
            
            throw new UsernameNotFoundException("User " + username + " has no GrantedAuthority");
        }
        
        return createUserDetails(username, account, dbAuths);
    }
    
    protected List<UserDetails> loadUsersByUsername(String username) {
        List<User> userEntityList = userDao.getUserByUsername(username);
        List<UserDetails> userDetailsList = new ArrayList<UserDetails>();
        for (User user : userEntityList) {
            userDetailsList.add(new Account(user.getUsername(), user.getPassword(),
                    user.getEnabled(), true, true, true,
                    AuthorityUtils.NO_AUTHORITIES, user.getId()));
        }
        return userDetailsList;
    }

    protected List<GrantedAuthority> loadUserAuthorities(String username) {
        List<Authority> authorities = authorityDao.getAuthoritiesByUsername(username);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
        String roleName = null;
        for (Authority authority : authorities) {
            roleName = rolePrefix + authority.getAuthority();
            GrantedAuthorityImpl grantedAuthority = new GrantedAuthorityImpl(roleName);
            grantedAuthorities.add(grantedAuthority);
        }
        return grantedAuthorities;
    }
    
    
    protected List<GrantedAuthority> loadGroupAuthorities(String username) {
        //TODO implement the group authority
        return new ArrayList<GrantedAuthority>();
    }
    
    protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) {
        
    }

    protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery,
            List<GrantedAuthority> combinedAuthorities) {
        String returnUsername = userFromUserQuery.getUsername();
        
        if (!usernameBasedPrimaryKey) {
            returnUsername = username;
        }
        return new Account(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(),
                true, true, true, combinedAuthorities, ((Account)userFromUserQuery).getUserId());
    }

    public String getAuthoritiesByUsernameJQuery() {
        return authoritiesByUsernameJQuery;
    }

    public void setAuthoritiesByUsernameJQuery(String authoritiesByUsernameJQuery) {
        this.authoritiesByUsernameJQuery = authoritiesByUsernameJQuery;
    }

    public String getUsersByUsernameJQuery() {
        return usersByUsernameJQuery;
    }

    public void setUsersByUsernameJQuery(String usersByUsernameJQuery) {
        this.usersByUsernameJQuery = usersByUsernameJQuery;
    }
    
    public void setGroupAuthoritiesByUsernameJQuery(String groupAuthoritiesByUsernameJQuery) {
        this.groupAuthoritiesByUsernameJQuery = groupAuthoritiesByUsernameJQuery;
    }

    public String getRolePrefix() {
        return rolePrefix;
    }

    public void setRolePrefix(String rolePrefix) {
        this.rolePrefix = rolePrefix;
    }

    public boolean isUsernameBasedPrimaryKey() {
        return usernameBasedPrimaryKey;
    }

    public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
        this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
    }

    public boolean getEnableAuthorities() {
        return enableAuthorities;
    }

    public void setEnableAuthorities(boolean enableAuthorities) {
        this.enableAuthorities = enableAuthorities;
    }

    public boolean getEnableGroups() {
        return enableGroups;
    }

    public void setEnableGroups(boolean enableGroups) {
        this.enableGroups = enableGroups;
    }
    
}
